package http;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 非常简单的HTTP服务器
 * 监听80端口，按照阻塞模式工作，采用线程池来处理每个客户请求
 * */
public class SimpleHttpServer {
    private int port = 80;
    private ServerSocketChannel serverSocketChannel = null;
    private ExecutorService executorService;
    private static final int POOL_MULTIPLE = 4;
    private final Charset charset = StandardCharsets.UTF_8;

    public SimpleHttpServer() throws IOException {
        executorService = Executors.newFixedThreadPool(
                Runtime.getRuntime().availableProcessors() * POOL_MULTIPLE);
        serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().setReuseAddress(true);
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
        System.out.println("服务器启动");
    }

    public void service() {
        while (true) {
            SocketChannel socketChannel = null;
            try {
                socketChannel = serverSocketChannel.accept();
                executorService.execute(new Handler(socketChannel));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 编码
     * */
    private ByteBuffer encode(String str) {
        return charset.encode(str);
    }

    /**
     * 解码
     * */
    private String decode(ByteBuffer buffer) {
        CharBuffer charBuffer = charset.decode(buffer);
        return charBuffer.toString();
    }

    public static void main(String[] args) throws IOException {
        new SimpleHttpServer().service();
    }

    /**
     * 内部类
     * 负责处理HTTP请求
     * */
    private class Handler implements Runnable {
        private SocketChannel socketChannel;

        public Handler(SocketChannel socketChannel) {
            this.socketChannel = socketChannel;
        }

        @Override
        public void run() {
            handle(socketChannel);
        }

        private void handle(SocketChannel socketChannel) {
            try (SocketChannel ignored = socketChannel) {
                Socket socket = socketChannel.socket();
                System.out.println("接收到客户连接，来自：" + socket.getInetAddress()
                        + ":" + socket.getPort());

                ByteBuffer buffer = ByteBuffer.allocate(1024);
                // 接收HTTP请求，假定其长度不超过1024字节
                socketChannel.read(buffer);
                buffer.flip();
                String request = decode(buffer);
                System.out.println(request);

                // 生成HTTP响应结果
                StringBuffer stringBuffer = new StringBuffer("http/1.1 200 OK\r\n");
                stringBuffer.append("Content-Type:text/html\r\n\r\n");

                // 发送HTTP响应的第1行和响应头
                socketChannel.write(encode(stringBuffer.toString()));

                FileInputStream in;
                // 获取HTTP请求的第1行
                String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
                if (firstLineOfRequest.indexOf("login.htm") != -1)
                    in = new FileInputStream("login.htm");
                else
                    in = new FileInputStream("hello.htm");

                // 发送响应正文
                FileChannel fileChannel = in.getChannel();
                fileChannel.transferTo(0, fileChannel.size(), socketChannel);   // 搬运、复制到输出流
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
